//
// Created by LiuYou on 2021/10/22.
//

#include "MyTbf.hpp"
#include <cstdlib>
#include <unistd.h>
#include <algorithm>
#include <csignal>
#include <sys/time.h>
#include <cstdio>


class MyTbf {
public:
    MyTbf() = default;

    ~MyTbf() = default;

public:
    int cps{};
    int burst{};
    int token{};
    int pos{};
};


static MyTbf* tbfPolicyArray[MY_TBF_MAX];
static int inited = 0;
//static sighandler_t alarmHandlerSave;
static struct sigaction saSave;


static int getFreePos() {
    for (int i = 0; i < MY_TBF_MAX; ++i) {
        if (tbfPolicyArray[i] == nullptr) {
            return i;
        }
    }
    return -1;
}


[[deprecated("使用 alarmHandlerSigAction 代替使用此函数。")]]
static void alarmHandler(int sig) {
    for (int i = 0; i < MY_TBF_MAX; ++i) {
        if (tbfPolicyArray[i] != nullptr) {
            tbfPolicyArray[i]->token += tbfPolicyArray[i]->cps;
            if (tbfPolicyArray[i]->token > tbfPolicyArray[i]->burst) {
                tbfPolicyArray[i]->token = tbfPolicyArray[i]->burst;
            }
        }
    }
//    alarm( 1 );

}


static void
alarmHandlerSigAction(int signal, siginfo_t* ptrSigInfo, void* unused/* 这个参数设计者其实不想让我们用了。 */) {
    // todo 笔记: 只响应从 kernel 来的信号。
    if (ptrSigInfo->si_code != SI_KERNEL) {
        return;
    }

    for (int i = 0; i < MY_TBF_MAX; ++i) {
        if (tbfPolicyArray[i] != nullptr) {
            tbfPolicyArray[i]->token += tbfPolicyArray[i]->cps;
            if (tbfPolicyArray[i]->token > tbfPolicyArray[i]->burst) {
                tbfPolicyArray[i]->token = tbfPolicyArray[i]->burst;
            }
        }
    }

}


static void moduleUnload() {
//    signal( SIGALRM, alarmHandlerSave );
//    alarm( 0 );

    sigaction( SIGALRM, &saSave, nullptr );


    itimerval itv{};
    itv.it_interval.tv_sec = 0;
    itv.it_interval.tv_usec = 0;
    itv.it_value.tv_sec = 0;
    itv.it_value.tv_usec = 0;
    setitimer( ITIMER_REAL, &itv, nullptr );


    for (int i = 0; i < MY_TBF_MAX; ++i) {
        // todo 笔记: free(nullptr); 是没有任何影响的。
        free( tbfPolicyArray[i] );
    }
}


static void moduleLoad() {
//    alarmHandlerSave = signal( SIGALRM, alarmHandler );
//    alarm( 1 );

    struct sigaction sa{};
//    sa.sa_handler = alarmHandler;
    sa.sa_sigaction = alarmHandlerSigAction;
    sigemptyset( &sa.sa_mask );
    // todo 笔记: 如果使用的三参的信号处理函数, 那么就必须指定 sa_flags 为 SA_SIGINFO。
    sa.sa_flags = SA_SIGINFO;

//    struct sigaction saSave{};
    int ret = sigaction( SIGALRM, &sa, &saSave );
    if (ret < 0) {
        perror( "sigaction()" );
        exit( 1 );
    }


    itimerval itv{};
    itv.it_interval.tv_sec = 1;
    itv.it_interval.tv_usec = 0;
    itv.it_value.tv_sec = 1;
    itv.it_value.tv_usec = 0;
    ret = setitimer( ITIMER_REAL, &itv, nullptr );
    if (ret < 0) {
        perror( "setitimer()" );
        exit( 1 );
    }


    atexit( moduleUnload );
}


Tbf* tbfInit(int cps, int burst) {
    if (!inited) {
        moduleLoad();
        inited = 1;
    }

    int pos = getFreePos();
    if (pos < 0) {
        return nullptr;
    }


    MyTbf* anInstance = static_cast<MyTbf*>(malloc( sizeof( MyTbf )));
    if (anInstance == nullptr) {
        return nullptr;
    }

    anInstance->token = 0;
    anInstance->cps = cps;
    anInstance->burst = burst;
    anInstance->pos = pos;

    tbfPolicyArray[pos] = anInstance;

    return anInstance;
}

int tbfDestroy(Tbf* tbf) {
    //<editor-fold desc="这样写也对但是不是一种好的设计。">
//    for (int i = 0; i < MY_TBF_MAX; ++i) {
//        if (tbfPolicyArray[i] == tbf) {
//            free( tbf );
//            tbfPolicyArray[i] = nullptr;
//        }
//    }
//    return 0;
    //</editor-fold>

    // todo 笔记:不要直接使用 tbf 指针变量参数, 因为它是 void* 类型！
    //  void* 不能取成员！！！！
    MyTbf* anInstance = static_cast<MyTbf*>(tbf);
    tbfPolicyArray[anInstance->pos] = nullptr;
    free( tbf );
    return 0;
}

int fetchToken(Tbf* tbf, int tokens) {
    if (tokens <= 0) {
        return -1;
    }

    // todo 笔记:不要直接使用 tbf 指针变量参数, 因为它是 void* 类型！
    //  void* 不能取成员！！！！
    //  这是进行类型隐藏的代价！每次都要进行强转。
    MyTbf* anInstance = static_cast<MyTbf*>(tbf);
    while (anInstance->token <= 0) {
        pause();
    }

    int min = std::min( anInstance->token, tokens );
    anInstance->token -= min;

    return min;
}

int returnToken(Tbf* tbf, int tokens) {
    if (tokens <= 0) {
        return -1;
    }

    MyTbf* me = static_cast<MyTbf*>(tbf);
    me->token += tokens;
    if (me->token > me->burst) {
        me->token = me->burst;
    }

    return tokens;
}
